from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jose import JWTError, jwt
from datetime import datetime, timedelta
from typing import Optional, AsyncGenerator

from config import SECRET_KEY, ALGORITHM, ACCESS_TOKEN_EXPIRE_MINUTES
from models import UserLogin, Token, TokenData, SearchRequest, NodeUpdateRequest
from neo4j_utils import Neo4jUtils
from sqlite_utils import SQLiteUtils  # 替换为SQLite工具类

# 初始化应用
app = FastAPI(title="Neo4j Graph Database Manager API")

# 安全配置
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

# 初始化数据库工具（使用SQLite替代MySQL）
neo4j_utils = Neo4jUtils()
sqlite_utils = SQLiteUtils()

# 定义生命周期事件处理器
@app.lifespan
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
    # 应用启动时执行 - 这里我们已经在初始化时建立了连接
    yield
    # 应用关闭时执行
    neo4j_utils.close()
    sqlite_utils.close()  # 关闭SQLite连接

# JWT工具函数
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(minutes=15)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

async def get_current_user(token: str = Depends(oauth2_scheme)):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise credentials_exception
        token_data = TokenData(username=username)
    except JWTError:
        raise credentials_exception
    user = sqlite_utils.get_user(username=token_data.username)  # 使用SQLite工具类
    if user is None:
        raise credentials_exception
    return user

# 认证接口
@app.post("/token", response_model=Token)
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
    user = sqlite_utils.get_user(username=form_data.username)  # 使用SQLite工具类
    if not user or not sqlite_utils.verify_password(form_data.password, user["password_hash"]):  # 使用SQLite工具类
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Incorrect username or password",
            headers={"WWW-Authenticate": "Bearer"},
        )
    access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    access_token = create_access_token(
        data={"sub": user["username"]}, expires_delta=access_token_expires
    )
    return {"access_token": access_token, "token_type": "bearer"}

# 节点查询接口
@app.post("/search-nodes")
async def search_nodes(
    request: SearchRequest, 
    current_user: dict = Depends(get_current_user)
):
    """根据查询字符串搜索节点及其上下游关系"""
    if not request.query.strip():
        raise HTTPException(status_code=400, detail="Query string cannot be empty")
    
    nodes = neo4j_utils.search_nodes(request.query)
    return {"nodes": nodes, "count": len(nodes)}

# 节点更新接口
@app.post("/update-node")
async def update_node(
    request: NodeUpdateRequest, 
    current_user: dict = Depends(get_current_user)
):
    """更新节点属性"""
    if not request.properties:
        raise HTTPException(status_code=400, detail="Properties cannot be empty")
    
    result = neo4j_utils.update_node(request.node_id, request.properties)
    return result

if __name__ == "__main__":
    import uvicorn
    from config import API_HOST, API_PORT
    uvicorn.run(app, host=API_HOST, port=API_PORT)
